﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Serialization;

namespace ExpressionSerialization
{
  public static class ExpressionExtension
  {
    public static Expression LogicalAnd(this Expression operand1, Expression operand2)
    {
      if(operand1 == null) return operand2;
      if(operand2 == null) return operand1;
      if(operand1 is LambdaExpression) operand1 = (operand1 as LambdaExpression).Body;
      if(operand2 is LambdaExpression) operand2 = (operand2 as LambdaExpression).Body;
      return  Expression.MakeBinary(ExpressionType.AndAlso, operand1, operand2); ;
    }

    public static Expression LogicalOr(this Expression operand1, Expression operand2)
    {
      if(operand1 == null) return operand2;
      if(operand2 == null) return operand1;
      if(operand1 is LambdaExpression) operand1 = (operand1 as LambdaExpression).Body;
      if(operand2 is LambdaExpression) operand2 = (operand2 as LambdaExpression).Body;
      return Expression.MakeBinary(ExpressionType.OrElse, operand1, operand2); ;
    }
    
    public static string SerializeToString(this Expression source_)
    {
      var aa = source_.Serialize();
      return System.Text.Encoding.UTF8.GetString(aa, 0, aa.Length);
    }

    public static byte[] Serialize(this Expression source_)
    {
      // 1.převedu na vnitřní objekty
      XExpression xexp = XExpression.CreateFromExpression(source_); //Convert.ExpressionToPlaneExpression(source_); 
      // 2.serializace objektů
      System.IO.MemoryStream ms = new System.IO.MemoryStream();
      DataContractSerializer dcs = new DataContractSerializer(typeof(XExpression));
      dcs.WriteObject(ms, xexp);
      byte[] res = ms.ToArray();
      ms.Close();
      return res;
    }

    public static Expression DeserializeFromString(string serializedValues)
    {
      var parameters = new List<ParameterExpression>(); // asi to nebudu potřebovat
      return ExpressionExtension.Deserialize(System.Text.Encoding.UTF8.GetBytes(serializedValues), null, parameters);
    }

    public static Expression DeserializeFromString(string serializedValues, Type dataContract_, List<ParameterExpression> parameters_)
    {
      return ExpressionExtension.Deserialize(System.Text.Encoding.UTF8.GetBytes(serializedValues),dataContract_, parameters_);
    }

    public static Expression Deserialize(byte[] serializedValues, Type dataContract_)
    {
      var parameters = new List<ParameterExpression>(); // asi to nebudu potřebovat
      return Deserialize(serializedValues, dataContract_, parameters);
    }

    public static Expression Deserialize(byte[] serializedValues, Type dataContract_, List<ParameterExpression> parameters_)
    {
      System.IO.MemoryStream ms = new System.IO.MemoryStream(serializedValues);
      DataContractSerializer dcs = new DataContractSerializer(typeof(XExpression));
      XExpression xexp = dcs.ReadObject(ms) as XExpression;
      return xexp.ToExpression(dataContract_, parameters_);
    }

    public static IQueryable<TSource> ApplyFilter<TSource>(this IQueryable<TSource> source, byte[] filter)
    {
      var parameters = new List<ParameterExpression>();
      Expression expr = ExpressionSerialization.ExpressionExtension.Deserialize(filter, source.ElementType, parameters);

      if( !(expr is LambdaExpression)) {//Expression<Func<TSource, bool>>
         expr = Expression.Lambda<Func<TSource, bool>>(expr, parameters);
      }
      source = source.Where(expr as Expression<Func<TSource, bool>>);
      return source;
    }

  }
}
